from django.shortcuts import render
from rest_framework.views import APIView
from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework import status
import base64
import pickle

from . import serializers
from goods.models import SKU
# Create your views here.


class CartView(APIView):
    """购物车后端:增删改查"""

    def perform_authentication(self, request):
        """
        重写父类的用户验证方法，不在进入视图前就检查JWT
        保证用户未登录也可以进入下面的请求方法，不让他执行request.user方法
        """
        pass

    def post(self, request):
        """新增"""
        # 新建序列化器对象
        serializer = serializers.CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 读取校验之后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 判断用户是否是登录的
        # request.user : 当用户是登录的用户，就会获取到登录用户的信息，反之，会抛出异常或者返回匿名用户
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录，操作redis
            # 获取连接到数据库的对象
            redis_conn = get_redis_connection('carts')
            # 管道
            pl = redis_conn.pipeline()

            # 操作hash类型的数据，存储哪个用户的哪件商品数量
            # hincrby() ： 实现增量计算，根据key是否存在，实现是累加还是赋新值
            pl.hincrby('cart_%s' % user.id, sku_id, count)
            # 存储该商品是否勾选
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            # 执行
            pl.execute()

            # 响应结果：查询和修改 200， 新增是 201, 删除是204
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            # 用户未登录，操作cookie
            # 从cookie中获取已存在的购物车数据
            cart_str = request.COOKIES.get('cart') # cart_str = (str)udHEDSwpYCAAA'
            if cart_str:
                # 将cart_str字符串转成二进制类型的字符串 b'udHEDSwpYCAAA'
                # cart_str.encode()
                # 再将b'udHEDSwpYCAAA'转成b'\x80\x03}q\x00(X\x01\x00\x00\x001q\x01}
                # base64.b64decode(cart_str.encode())
                # 将b'\x80\x03}q\x00(X\x01\x00\x00\x001q\x01}转成字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 表示用户从来没有在cookie中存储过购物车数据
                cart_dict = {}

            # 将前端传入的sku_id,count,selected存储到cookie
                # {
                #     sku_id10: {
                #         "count": 10, // 数量
                #         "selected": True // 是否勾选
                #     },
                #     sku_id20: {
                #         "count": 20,
                #         "selected": False
                #     },
                #     ...
                # }
            if sku_id in cart_dict:
                # 取出原始的值，进行累加。
                origin_count = cart_dict[sku_id]['count']
                count += origin_count

            # 将累加或者最新的count保存到字典
            cart_dict[sku_id] = {
                'count':count,
                'selected':selected
            }

            # 将购物车字典转正字符串
            # (dict){'1': {'count': 10, 'selected': True}, '2': {'count': 20, 'selected': False}}
            # (str)gAN9cQAoWAEAAAAxcQF9cQIoWAUAAABjb3VudHEDSwpYCAAAAHNlbGVjdGVkcQSIdVgBAAAAMnEFfXEGKGgDSxRoBIl1dS4=
            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            # 创建response对象
            response = Response(serializer.data, status=status.HTTP_201_CREATED)

            # 将新的购物车数据写入到cookie
            response.set_cookie('cart', cookie_cart_str)

            # 响应结果
            return response


    def get(self, request):
        """查询
        """
        # 判断用户是否是登录的
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录，操作redis
            # 获取连接到数据库的对象
            redis_conn = get_redis_connection('carts')
            # 查询所有的购物车数据
            redis_cart = redis_conn.hgetall('cart_%s' % user.id)
            # {
            #     b'sku_id':b'count',
            #     b'sku_id': b'count'
            # }
            # 查询哪些商品被勾选
            # cart_selected = [sku_id,sku_id]
            cart_selected = redis_conn.smembers('selected_%s' % user.id)

            # {
            #     sku_id10: {
            #         "count": 10, // 数量
            #         "selected": True // 是否勾选
            #     },
            #     sku_id20: {
            #         "count": 20,
            #         "selected": False
            #     },
            #     ...
            # }

            # 将redis_cart和cart_selected内部的数据，整合到cart_dict，实现格式的统一,方便后续查询sku信息
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count':int(count),
                    'selected':sku_id in cart_selected # 当sku_id在cart_selected中，说明该商品勾选
                }
        else:
            # 用户未登录，操作cookie
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 表示用户从来没有在cookie中存储过购物车数据
                cart_dict = {}

        # 根据cart_dict,查询购物车中的商品数据
        sku_ids = cart_dict.keys()
        # 查询出id在sku_ids中的所有的商品，构造我列表
        skus = SKU.objects.filter(id__in=sku_ids)

        # 为了能够在序列化skus的同时又序列化count和selected，就需要将他们追加到sku
        for sku in skus:
            sku.count = cart_dict[sku.id]['count']
            sku.selected = cart_dict[sku.id]['selected']

        # 创建序列化器对象，对skus进行序列化
        serializer = serializers.CartSKUSerializer(skus, many=True)
        # 响应数据， 状态码200
        return Response(serializer.data)


    def put(self, request):
        """修改"""
        # 新建序列化器对象
        serializer = serializers.CartSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 读取校验之后的数据
        sku_id = serializer.validated_data.get('sku_id')
        count = serializer.validated_data.get('count')
        selected = serializer.validated_data.get('selected')

        # 判断用户是否是登录的
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录，操作redis
            # 获取连接到数据库的对象
            redis_conn = get_redis_connection('carts')
            # 管道
            pl = redis_conn.pipeline()
            # 修改商品的数量 ： 后端使用幂等的接口设计方案，要求前端将用户最终在界面的上的结果发送给我们，直接覆盖写入。不做增量计算
            pl.hset('cart_%s' % user.id, sku_id, count)
            # 修改商品是否勾选
            if selected:
                pl.sadd('selected_%s' % user.id, sku_id)
            else:
                # 如果前端传入的是false,那么就把sku_id从列表中移除，表示该商品未勾选
                pl.srem('selected_%s' % user.id, sku_id)

            # 记住:需要调用excute()
            pl.execute()

            # 响应，200
            return Response(serializer.data)
        else:
            # 用户未登录，操作cookie
            cart_str = request.COOKIES.get('cart')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                # 表示用户从来没有在cookie中存储过购物车数据
                cart_dict = {}

            # 使用幂等的方式，覆盖写入新的数据
            cart_dict[sku_id] = {
                'count':count,
                'selected':selected
            }

            # 将新的购物车数据写入到cookie
            cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

            # 创建response对象
            response = Response(serializer.data)

            # 将新的购物车数据写入到cookie
            response.set_cookie('cart', cookie_cart_str)

            # 响应结果
            return response


    def delete(self, request):
        """删除"""
        # 创建序列化器对象
        serializer = serializers.CartDeleteSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        # 读取校验后的sku_id
        sku_id = serializer.validated_data.get('sku_id')

        # 判断用户是否是登录的
        try:
            user = request.user
        except Exception:
            user = None

        if user is not None and user.is_authenticated:
            # 用户已登录，操作redis
            # 获取连接到数据库的对象
            redis_conn = get_redis_connection('carts')
            # 管道
            pl = redis_conn.pipeline()
            # 删除某一条记录
            pl.hdel('cart_%s' % user.id, sku_id)
            # 移除勾选的标记
            pl.srem('selected_%s' % user.id, sku_id)
            # 执行
            pl.execute()

            # 响应：只响应状态码204,因为数据已经被删除，没有序列化的数据了
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            # 用户未登录，cookie操作购物车
            cart_str = request.COOKIES.get('cart')

            if cart_str:  # 用户操作过cookie购物车
                # 将cart_str转成bytes,再将bytes转成base64的bytes,最后将bytes转字典
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:  # 用户从没有操作过cookie购物车
                cart_dict = {}

            # 创建响应对象
            response = Response(status=status.HTTP_204_NO_CONTENT)

            if sku_id in cart_dict:
                del cart_dict[sku_id]

                # 将字典转成bytes,再将bytes转成base64的bytes,最后将bytes转字符串
                cookie_cart_str = base64.b64encode(pickle.dumps(cart_dict)).decode()

                # 响应结果并将购物车数据写入到cookie
                response.set_cookie('cart', cookie_cart_str)

            return response






















